package org.lemon.transport.http.service;

import lombok.extern.slf4j.Slf4j;
import org.lemon.transport.http.service.callback.DeviceAuthCallback;
import org.lemon.transport.context.data.device.DeviceTransportType;
import org.lemon.transport.context.data.ota.OtaPackageType;
import org.server.transport.gen.TransportProto;
import org.lemon.transport.context.service.TransportServiceCallback;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.async.DeferredResult;

import java.util.UUID;

@Slf4j
@Service
public class OtaPackageService {

    @Autowired
    private HttpTransportContext transportContext;

    public DeferredResult<ResponseEntity<ByteArrayResource>> getOtaPackage(String deviceToken, String title, String version, int size, int chunk, OtaPackageType firmwareType) {

        DeferredResult<ResponseEntity<ByteArrayResource>> responseWriter = new DeferredResult<>();
        transportContext.getTransportService()
                .deviceTokenValidate(DeviceTransportType.DEFAULT, deviceToken, new DeviceAuthCallback<>(transportContext, responseWriter, (sessionInfo) -> {
                    TransportProto.GetOtaPackageRequestMsg requestMsg = TransportProto.GetOtaPackageRequestMsg.newBuilder()
                            .setTenantId(sessionInfo.getTenantId())
                            .setDeviceId(sessionInfo.getDeviceId())
                            .setType(firmwareType.name()).build();
                    transportContext.getTransportService()
                            .process(sessionInfo, requestMsg, new TransportServiceCallback<TransportProto.GetOtaPackageResponseMsg>() {

                                @Override
                                public void onSuccess(TransportProto.GetOtaPackageResponseMsg otaPackageResponseMsg) {
                                    if (!TransportProto.ResponseStatus.SUCCESS.equals(otaPackageResponseMsg.getResponseStatus())) {
                                        responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_FOUND));
                                    } else if (title.equals(otaPackageResponseMsg.getTitle()) && version.equals(otaPackageResponseMsg.getVersion())) {
                                        String otaPackageId = new UUID(otaPackageResponseMsg.getOtaPackageId(), otaPackageResponseMsg.getOtaPackageId()).toString();

                                        ByteArrayResource resource = new ByteArrayResource(transportContext.getOtaPackageDataCache().get(otaPackageId, size, chunk));
                                        ResponseEntity<ByteArrayResource> response = ResponseEntity.ok()
                                                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + otaPackageResponseMsg.getFileName())
                                                .header("x-filename", otaPackageResponseMsg.getFileName())
                                                .contentLength(resource.contentLength())
                                                .contentType(parseMediaType(otaPackageResponseMsg.getContentType()))
                                                .body(resource);
                                        responseWriter.setResult(response);
                                    } else {
                                        responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
                                    }
                                }

                                @Override
                                public void onError(Throwable e) {
                                    log.warn("Failed to process request", e);
                                    responseWriter.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
                                }
                            });
                }));
        return responseWriter;
    }

    private static MediaType parseMediaType(String contentType) {
        try {
            return MediaType.parseMediaType(contentType);
        } catch (Exception e) {
            return MediaType.APPLICATION_OCTET_STREAM;
        }
    }
}
